package com.tomato.gateway.loadbalancer;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.*;
import org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.http.HttpHeaders;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * 自定义负载均衡算法实现灰度
 * 获取请求头中的version属性，然后根据服务实例元数据中的version属性进行匹配
 * 参考：
 * org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer#getInstanceResponse
 * @author lizhifu
 * @date 2022/3/12
 */
@Slf4j
public class VersionRoundRobinLoadBalancer implements ReactorServiceInstanceLoadBalancer {
    private final ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;
    private final String serviceId;
    private final AtomicInteger position;

    public VersionRoundRobinLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) {
        this.serviceId = serviceId;
        this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
        this.position = new AtomicInteger(new Random().nextInt(1000));
    }

    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
        ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);
        return supplier.get(request).next().map(list -> getInstanceResponse(list, request));
    }

    private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances, Request request) {
        // 首先判断是否存在实例
        if (instances.isEmpty()) {
            log.warn("没有可供服务的实例: {}" , this.serviceId);
            return new EmptyResponse();
        }
        DefaultRequestContext requestContext = (DefaultRequestContext) request.getContext();
        RequestData clientRequest = (RequestData) requestContext.getClientRequest();
        HttpHeaders headers = clientRequest.getHeaders();

        // 获取请求头中的版本号
        String reqVersion = headers.getFirst("version");
        if(reqVersion == null || "".equals(reqVersion)){
            log.info("没有指定version : {} ", reqVersion);
            log.info("服务实例: {},没有指定version : {} ",this.serviceId,reqVersion);
            return processRibbonInstanceResponse(instances);
        }
        // 符合 version 的实例
        List<ServiceInstance> serviceInstances = instances.stream()
                .filter(instance -> reqVersion.equals(instance.getMetadata().get("version")))
                .collect(Collectors.toList());
        if(serviceInstances.size() > 0){
            log.info("服务实例: {},符合 version 的实例 : {} 个", this.serviceId, instances.size());
            return processRibbonInstanceResponse(serviceInstances);
        }else{
            log.info("服务实例: {},没有符合 version 的实例 : {} ", this.serviceId,reqVersion);
            return processRibbonInstanceResponse(instances);
        }
    }

    /**
     * 负载均衡器
     */
    private Response<ServiceInstance> processRibbonInstanceResponse(List<ServiceInstance> instances) {
        int pos = Math.abs(this.position.incrementAndGet());
        ServiceInstance instance = instances.get(pos % instances.size());
        return new DefaultResponse(instance);
    }
}
